#include "precompile.h"

#include <vsys>

#include "SimpleFileStream.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SimpleFileStream::SimpleFileStream() :
	m_hFile(INVALID_HANDLE_VALUE),
	m_hFileMap(NULL)
{
	m_lCurPos = 0;
	m_lLength = 0;
	SYSTEM_INFO SysInfo;
	GetSystemInfo(&SysInfo);
	m_lGran = 20*SysInfo.dwAllocationGranularity;
	m_pData = NULL;
	m_lFileOffset = 0;
	m_lDataLength = 0;
	m_bIsNetFile = false;
}

SimpleFileStream::~SimpleFileStream()
{
	Close();
}

vresult SimpleFileStream::Close()
{
	if(m_hFile != INVALID_HANDLE_VALUE )
	{
		if( NULL != m_hFileMap)
		{
			if( NULL != m_pData )
			{
				UnmapViewOfFile(m_pData);
				m_pData = NULL;
			}
			CloseHandle(m_hFileMap);
			m_hFileMap = NULL;
		}
		CloseHandle(m_hFile);
		m_hFile=INVALID_HANDLE_VALUE;
	}
	return vok;
}

vresult SimpleFileStream::Flush()
{
	if( NULL != m_pData )
		UnmapViewOfFile(m_pData);
	m_pData = (byte*)MapViewOfFile(m_hFileMap,FILE_MAP_ALL_ACCESS, 0, m_lFileOffset, m_lDataLength);
//	if(FlushViewOfFile(m_pData, m_lCurPos-m_lFileOffset))
		return vok;
//	return err_flushfailed;
}

vresult SimpleFileStream::SaveByte(byte by)
{
	long lSaveLen=0;
	return SaveData(&by,sizeof(byte),&lSaveLen);
}

vresult SimpleFileStream::LoadByte(byte* pByte)
{
	long lLoadLen=0;
	return LoadData(pByte, sizeof(byte),&lLoadLen);
}

void SimpleFileStream::RefereshMapView(long lDataLen)
{
	if(m_lCurPos+lDataLen >= m_lLength)
	{
		/*UnmapViewOfFile(m_pData);
		CloseHandle(m_hFileMap);*/
		long lLength = ((m_lCurPos+lDataLen+1)/m_lGran+((m_lCurPos+lDataLen+1)%m_lGran?1:0))*m_lGran;
		SetStreamLength(lLength);
		m_hFileMap = CreateFileMapping(m_hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
		if(m_lCurPos%m_lGran==0)
			m_lFileOffset = m_lCurPos;
		else
			m_lFileOffset = m_lCurPos/m_lGran*m_lGran;
		m_lDataLength = m_lLength-m_lFileOffset;
		m_pData = (byte*)MapViewOfFile(m_hFileMap,FILE_MAP_ALL_ACCESS, 0, m_lFileOffset, m_lDataLength);
	}
	else if((m_lCurPos+lDataLen > m_lFileOffset+m_lDataLength)
		||(m_lCurPos < m_lFileOffset))
	{
		if ( NULL != m_pData )
			UnmapViewOfFile(m_pData);
		if(m_lCurPos%m_lGran==0)
			m_lFileOffset = m_lCurPos;
		else
			m_lFileOffset = m_lCurPos/m_lGran*m_lGran;
		long lNeedSize = m_lCurPos+lDataLen-m_lFileOffset;
		m_lDataLength = max((lNeedSize/m_lGran+(lNeedSize%m_lGran?1:0))*m_lGran, m_lGran);
		m_pData = (byte*)MapViewOfFile(m_hFileMap,FILE_MAP_ALL_ACCESS, 0, m_lFileOffset, m_lDataLength);
	}
}

vresult SimpleFileStream::SaveData(const byte* pByte,long lDataLen,long* pDataSaved)
{
	RefereshMapView(lDataLen);
	memcpy(m_pData+m_lCurPos-m_lFileOffset, pByte, lDataLen);
	m_lCurPos+=lDataLen;
	*pDataSaved = lDataLen;
	return vok;
}

vresult SimpleFileStream::LoadData(byte* pByte,long lDataLen,long* pDataLoaded)
{
	RefereshMapView(lDataLen);
	*pDataLoaded = min(lDataLen, m_lLength-m_lCurPos);
	memcpy(pByte, m_pData+m_lCurPos-m_lFileOffset, *pDataLoaded);
	m_lCurPos+=*pDataLoaded;
	return vok;
}

long SimpleFileStream::GetCurrentPos()
{
	return m_lCurPos;
}

vresult SimpleFileStream::Seek(kSeekMode mode,long nNewPos)
{
	ASSERT( m_hFile!= INVALID_HANDLE_VALUE );
	long lMode = -1;

	switch (mode)
	{
	case kBegin:
		lMode=FILE_BEGIN;
		m_lCurPos = nNewPos;
		break;
	case kCurrent:
		lMode=FILE_CURRENT;
		m_lCurPos += nNewPos;
		break;
	case kEnd:
		lMode=FILE_END;
		m_lCurPos = m_lLength-nNewPos;
		break;
	default:
		ASSERT(false);
	}

	RefereshMapView(0);
	return vok;
}

long SimpleFileStream::GetStreamLength()
{
	return m_lLength;
}

vresult SimpleFileStream::SetStreamLength(long lNewLength)
{
	if(NULL != m_pData)
	{
		if(m_bIsNetFile) FlushViewOfFile(m_pData,m_lCurPos-m_lFileOffset);
		UnmapViewOfFile(m_pData);
		m_pData = NULL;
	}
	if(NULL != m_hFileMap)
	{
		CloseHandle(m_hFileMap);
		m_hFileMap = NULL;
	}
	m_lLength = lNewLength;
	SetFilePointer(m_hFile,lNewLength,NULL,FILE_BEGIN);

	if (SetEndOfFile(m_hFile))
		return vok;

	return vfailed;
}

vresult SimpleFileStream::InitMappingFile( uint32 dwMapAccess, uint32 dwViewAccess )
{
	m_lCurPos = 0;
	m_lFileOffset = 0;
	m_lLength = GetFileSize(m_hFile,NULL);
	m_lDataLength = m_lLength;
	if ( 0 != m_lLength)
	{
		m_hFileMap = CreateFileMapping(m_hFile, NULL, dwMapAccess, 0, 0, NULL);
		if( NULL == m_hFileMap )
		{
			int32 iErr;
			iErr = GetLastError();
			ASSERT(false);
			return err_openfailed;
		}
		m_pData = (byte*)MapViewOfFile(m_hFileMap,dwViewAccess,0, 0, m_lDataLength);
		if( NULL == m_pData )
		{
			int32 iErr = GetLastError();
			iErr;
			ASSERT(false);
			return err_openfailed;
		}
	}
	return vok;
}

vresult SimpleFileStream::OpenFile(const wchar_t* sFileName,bool bCreate,bool bTruncate_existing)
{
	ASSERT(m_hFile==INVALID_HANDLE_VALUE);
	uint32 dwCreateOption=0;
	if(bCreate)
		dwCreateOption=CREATE_ALWAYS;
	else
		dwCreateOption=bTruncate_existing?(TRUNCATE_EXISTING|OPEN_EXISTING) : OPEN_EXISTING;

	m_hFile=CreateFile(sFileName,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ,NULL,dwCreateOption,FILE_ATTRIBUTE_NORMAL,NULL);
	if(m_hFile==NULL || m_hFile==INVALID_HANDLE_VALUE)
		return err_openfailed;

	if( m_hFile==INVALID_HANDLE_VALUE )
	{
		int32 iErr;
		iErr = GetLastError();
		ASSERT(false);
		return err_openfailed;
	}
	return InitMappingFile(PAGE_READWRITE,FILE_MAP_ALL_ACCESS);

}

static bool IsRemotePath( const wchar_t* wstrFileName )
{
	String wstrRootName = wstrFileName;
	String::size_type pos = wstrRootName.rfind(L"\\");
	if( String::npos == pos ) return false;
	wstrRootName.resize(pos+1);
	return  DRIVE_REMOTE == ::GetDriveTypeW( wstrRootName.c_str() );
}

vresult SimpleFileStream::OpenFile(const wchar_t* sFileName,kDocOp docop)
{
	ASSERT(m_hFile == INVALID_HANDLE_VALUE);
	bool bReadOnly = false;
	uint32 dwCreateOption,dwShareAccess,dwFileAccess,dwMapAccess,dwViewAccess,dwFlagsAndAttribute;

	m_bIsNetFile = IsRemotePath(sFileName);
	if (docop == kDocLoadRead) bReadOnly = true;
	dwCreateOption= (docop == kDocCreate) ? CREATE_ALWAYS : OPEN_EXISTING;
	dwFileAccess= bReadOnly ? GENERIC_READ: GENERIC_READ|GENERIC_WRITE;
	dwShareAccess = bReadOnly ? FILE_SHARE_READ|FILE_SHARE_WRITE : FILE_SHARE_READ;
	dwMapAccess = bReadOnly ? PAGE_READONLY: PAGE_READWRITE;
	dwViewAccess = bReadOnly ? FILE_MAP_READ: FILE_MAP_ALL_ACCESS;
	dwFlagsAndAttribute = m_bIsNetFile ? (FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH) : FILE_ATTRIBUTE_NORMAL;

	m_hFile=CreateFile(sFileName,dwFileAccess,dwShareAccess,NULL,dwCreateOption,dwFlagsAndAttribute,NULL);
	if( m_hFile==INVALID_HANDLE_VALUE )
	{
		int32 iErr;
		iErr = GetLastError();
		ASSERT(false);
		return err_openfailed;
	}
	return InitMappingFile(dwMapAccess,dwViewAccess);
}

HANDLE SimpleFileStream::ReleaseFileHandle()
{
	HANDLE hFile = m_hFile;
	if( m_hFile != INVALID_HANDLE_VALUE )
	{
		if(m_hFileMap)
		{
			if( NULL != m_pData )
			{
				UnmapViewOfFile(m_pData);
				m_pData = NULL;
			}
			CloseHandle(m_hFileMap);
			m_hFileMap = NULL;
		}
		m_hFile=INVALID_HANDLE_VALUE;
	}
	return hFile;
}